package org.monkey.socket.plugin.provider;

import cn.hutool.core.util.StrUtil;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.websocketx.CloseWebSocketFrame;
import io.netty.util.concurrent.GlobalEventExecutor;
import java.util.HashMap;
import java.util.Map;
import lombok.extern.slf4j.Slf4j;
import org.monkey.common.constant.config.CommonConstant;
import org.monkey.common.constant.util.UrlUtil;
import org.monkey.socket.plugin.config.SocketConstant;
import org.monkey.socket.plugin.entity.WebSocketProperties;
import org.monkey.socket.plugin.service.IMsgChannelManage;

@Slf4j
public class DefaultMsgChannelManage implements IMsgChannelManage {
  public  Map<String, Map<String, ChannelGroup>> namespaceGroup = new HashMap<>();
  private WebSocketProperties webSocketProperties;

  public DefaultMsgChannelManage(
      WebSocketProperties webSocketProperties) {
    this.webSocketProperties = webSocketProperties;
  }

  @Override
  public void onLogin(ChannelHandlerContext ctx, FullHttpRequest request) {
    String uri = request.uri();
    Map<String, Object> params = UrlUtil.parse(uri).getParams();
    if (uri.startsWith(webSocketProperties.getPath())) {
      request.setUri(webSocketProperties.getPath());
      String userId = params.getOrDefault("id", CommonConstant.EMPTY_STRING).toString();
      String namespace = params.getOrDefault("namespace", CommonConstant.DEFAULT_SOCKET_NAMESPACE)
          .toString();
      if (StrUtil.isNotBlank(userId)) {
        log.info("get client connect request:namespace={},userId={}", namespace, userId);
        ctx.channel().attr(SocketConstant.USER_ID_KEY).setIfAbsent(userId);
        ctx.channel().attr(SocketConstant.NAME_SPACE_KEY).setIfAbsent(namespace);
        addChannel(ctx.channel());
        request.setUri(webSocketProperties.getPath());
      } else {
        request.setUri("/");
        ctx.close();
      }
    } else {
      ctx.close();
    }
  }

  @Override
  public void onClose(ChannelHandlerContext ctx, CloseWebSocketFrame frame) {
    removeChannel(ctx.channel());
  }

  @Override
  public void disConnected(ChannelHandlerContext ctx) {
    removeChannel(ctx.channel());
  }

  @Override
  public ChannelGroup getChannelGroupByArgs(String... args) {
    if(args.length==2) {
      String namespace = args[0];
      String userId = args[1];
      Map<String, ChannelGroup> channelGroupMap = namespaceGroup.get(namespace);
      if (channelGroupMap != null) {
        return channelGroupMap.get(userId);
      }
    }
    return null;
  }

  /**
   * 新增一个客户端通道
   *
   * @param channel
   */
  public  void addChannel(Channel channel) {
    String namespace = channel.attr(SocketConstant.NAME_SPACE_KEY).get();
    String userId = channel.attr(SocketConstant.USER_ID_KEY).get();
    log.info("register channel to manage:namespace={},userId={},channelId={}", namespace, userId,
        channel.id().asLongText());
    Map<String, ChannelGroup> channelGroupMap = namespaceGroup
        .getOrDefault(namespace, new HashMap<>());
    ChannelGroup channels = channelGroupMap
        .getOrDefault(userId, new DefaultChannelGroup(GlobalEventExecutor.INSTANCE));
    channels.add(channel);
    channelGroupMap.put(userId, channels);
    namespaceGroup.put(namespace, channelGroupMap);
  }

  /**
   * 移除一个客户端连接通道
   *
   * @param channel
   */
  public  void removeChannel(Channel channel) {
    String namespace = channel.attr(SocketConstant.NAME_SPACE_KEY).get();
    String userId = channel.attr(SocketConstant.USER_ID_KEY).get();
    if (StrUtil.isNotBlank(namespace) && StrUtil.isNotBlank(userId)) {
      Map<String, ChannelGroup> channelGroupMap = namespaceGroup.get(namespace);
      if (channelGroupMap != null) {
        ChannelGroup channels = channelGroupMap.get(userId);
        if (channels != null) {
          log.info("logout channel to manage:namespace={},userId={},channelId={}", namespace,
              userId, channel.id().asLongText());
          channels.remove(channel);
        }
      }
    }
  }
}
